package top.healthylife.gateway.platform.geo.api.util;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import top.healthylife.basics.utils.exception.BusinessException;
import top.healthylife.basics.utils.utils.RedisUtil;
import top.healthylife.basics.utils.utils.SystemThreadLocal;
import top.healthylife.gateway.platform.geo.api.config.SystemConfig;
import top.heathylife.gateway.common.cons.SystemCons;

import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 集奥 获取token,访问api的工具类
 *
 * @author Gaozexi
 */
public class TokenUtil {

    private static final String SUCCESS = "200";
    private static final String CODE = "code";
    private static final Integer RETRY = 3;
    private static final String TOKEN_KEY_PREFIX = "interface:api:geo:token:";
    /**
     * 接口请求返回的code码
     */
    static Map<String, String> apiCodeMap = new HashMap<>(64);

    static {
        apiCodeMap.put("200", "成功");
        apiCodeMap.put("-888", "登录失败,用户不存在");
        apiCodeMap.put("-999", "用户名不能为空");
        apiCodeMap.put("-747", "该用户已经删除");
        apiCodeMap.put("-700", "登陆出现异常");
        apiCodeMap.put("-666", "登陆失败,记录登陆信息失败");
        apiCodeMap.put("-555", "登录失败,用户名密码错误");
        apiCodeMap.put("-556", "图形验证码输入错误");
        apiCodeMap.put("-444", "登录失败,账户被锁定");
        apiCodeMap.put("-100", "token为空（需要重新获tokenID）");
        apiCodeMap.put("-200", "用户被新登录挤掉（需要重新获tokenID）");
        apiCodeMap.put("-300", "用户未登录（需要重新获tokenID）");
        apiCodeMap.put("-400", "用户未具备访问权限");
        apiCodeMap.put("500", "服务器错误");
        apiCodeMap.put("-41054", "注册失败");
        apiCodeMap.put("50001", "剩余使用量不足");
        apiCodeMap.put("50002", "接口查询接口类型参数配置错误");
        apiCodeMap.put("50003", "文件拷贝错误");
        apiCodeMap.put("50004", "业务类型为空");
        apiCodeMap.put("1100000", "授权码格式错误");
        apiCodeMap.put("1000000", "暂不支持此运营商");
        apiCodeMap.put("1000005", "type为空或type格式错误(正确示例：A1,A5,B1)");
        apiCodeMap.put("1000006", "手机号码错误(没有匹配的运营商)");
        apiCodeMap.put("1000020", "手机号不能为空");
        apiCodeMap.put("1000009", "手机号格式错误");
        apiCodeMap.put("1000019", "手机号2格式错误");
        apiCodeMap.put("1000021", "身份证号不能为空");
        apiCodeMap.put("1000007", "身份证号格式错误");
        apiCodeMap.put("1000008", "姓名不能为空");
        apiCodeMap.put("49999", "余额不足");
        apiCodeMap.put("100000801", "姓名不能为乱码");
        apiCodeMap.put("100000802", "姓名格式错误");
        apiCodeMap.put("501", "参数错误");
        apiCodeMap.put("10009", "系统异常");
        apiCodeMap.put("10010", "对象异常");
        apiCodeMap.put("10011", "其他异常");
        apiCodeMap.put("10012", "签名验证失败");
        apiCodeMap.put("10013", "数据查询超时");
        apiCodeMap.put("100000001", "不支持虚拟运营商");
        apiCodeMap.put("49998", "T、Y、Z、E系列接口不能和其他接口同时调用");
        apiCodeMap.put("50020", "回调接口和实时接口不能同时调用");
        apiCodeMap.put("1000074", "超出并发数");
        apiCodeMap.put("1000075", "超出当日最大调用量");
    }

    /**
     * 获取token地址
     */
    private static final String LOGIN_PATH = "http://yz.geotmt.com/civp/getview/api/o/login";


    /**
     * 请求数据
     *
     * @param url       .
     * @param paramsMap 参数
     * @return 响应json
     */
    @SneakyThrows
    public static <T> T getApiData(String url, Map<String, Object> paramsMap, SystemConfig systemConfig, Class<T> cls) {
        getToken(systemConfig, 0);
        String params = formatParamMap(paramsMap, systemConfig);
        String data = HttpRequest.post(url).body(params).execute().body();
        String decryptRs = AES.decrypt(data, systemConfig.getEncryptionKey());
        //将三方元数据存储到线程变量
        SystemThreadLocal.set(SystemCons.ORIGIN_DATA,decryptRs);
        if (ObjectUtils.isNotEmpty(decryptRs)) {
            JSONObject jsonObject = JSONObject.parseObject(decryptRs);
            String code = jsonObject.get("code").toString();
            if (SUCCESS.equals(code)) {
                JSONObject data1 = jsonObject.getJSONObject("data");
                return data1.toJavaObject(cls);
            } else {
                String desc = apiCodeMap.get(code);
                throw new BusinessException(desc == null ? "三方接口返回异常" : desc);
            }
        }
        return null;
    }


    /**
     * 格式化参数
     *
     * @param params     参数Map
     * @param geoAccount 账户信息
     * @return 格式化后的字符串
     */
    private static String formatParamMap(Map<String, Object> params, SystemConfig geoAccount) {
        params.put("authCode", geoAccount.getAuthCode());
        StringBuilder sb = new StringBuilder();
        Set<String> set = params.keySet();
        for (String k : set) {
            //参数加密
            String value = AES.encrypt(params.get(k).toString(), geoAccount.getEncryptionKey());
            sb.append(k).append("=").append(value).append("&");
        }
        sb.append("tokenId").append("=").append(geoAccount.getTokenId());
        return sb.toString();
    }


    /**
     * 登录集奥 获取token
     *
     * @param geoAccount 集奥的账户 tokenId 未获取
     * @param i          重试次数
     * @return 集奥的账户 tokenId 已获取
     */
    public static SystemConfig getToken(SystemConfig geoAccount, int i) {
        String aesKey = geoAccount.getEncryptionKey();
        String username = geoAccount.getUsername();
        String password = geoAccount.getPassword();
        String uno = geoAccount.getUno();
        String tokeKey = TOKEN_KEY_PREFIX + username + password + uno;
        //查看token是否存在
        if (RedisUtil.exists(tokeKey)) {
            String tokenId = RedisUtil.get(tokeKey).toString();
            geoAccount.setTokenId(tokenId);
            return geoAccount;
        }
        try {
            String eUsername = AES.encrypt(username, aesKey);
            String ePassword = AES.encrypt(password, aesKey);
            String etype = geoAccount.getEtype();
            KeyPair keyPair = RSAUtils.getKeyPair();
            String paraEncryptionType = RSAUtils.encrypt(keyPair.getPublic(), geoAccount.getEncryptionType());
            String paraEncryptionKey = RSAUtils.encrypt(keyPair.getPublic(), geoAccount.getEncryptionKey());
            String encrypted = String.valueOf(geoAccount.getEncrypted());
            String eDisgn = AES.encrypt(String.valueOf(geoAccount.getDsign()), aesKey);

            String params = String.format(
                    "username=%s" +
                            "&password=%s" +
                            "&etype=%s" +
                            "&encryptionType=%s" +
                            "&encryptionKey=%s" +
                            "&encrypted=%s" +
                            "&dsign=%s"
                    , eUsername, ePassword, etype, paraEncryptionType, paraEncryptionKey, encrypted, eDisgn);
            String rs = HttpRequest.post(LOGIN_PATH).body(params).execute().body();
            rs = AES.decrypt(rs, aesKey);
            JSONObject jsonObject = JSONObject.parseObject(rs);
            if (SUCCESS.equals(jsonObject.get(CODE).toString())) {
                String tokenId = jsonObject.get("tokenId") + "";
                //过期时间  23h
                RedisUtil.set(tokeKey, tokenId, 23, TimeUnit.HOURS);
                geoAccount.setTokenId(tokenId);
                return geoAccount;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (i <= RETRY) {
            //重试
            return getToken(geoAccount, ++i);
        }
        throw new BusinessException("集奥账户登录失败");
    }

}
